from typing import Optional

import sys
import traceback

import xml.etree.ElementTree as ET

import model
from model.uml_element import UmlElement
from model.uml_class import UmlClass
from model.uml_model import UmlModel
from model.uml_enumeration import UmlEnumeration
from model.uml_package import UmlPackage
from model.uml_package_import import UmlPackageImport
from model.uml_xmi_info import UmlXmiInfo
from utils.file_util import FileUtil


class UmlManager:
    uml_xmi_file = FileUtil.app_path("data", "uml", "UML-v2.5-UML.xmi")
    primitive_types_xmi_file = FileUtil.app_path(
        "data", "uml", "UML-v2.5-PrimitiveTypes.xmi"
    )
    profile_xmi_file = FileUtil.app_path("data", "uml", "UML-v2.5-StandardProfile.xmi")
    uml_di_xmi_file = FileUtil.app_path("data", "uml", "UML-v2.5-UMLDI.xmi")
    primitive_types = {
        model.UmlXmiInfo.type_boolean: "bool",
        model.UmlXmiInfo.type_integer: "int",
        model.UmlXmiInfo.type_real: "double",
        model.UmlXmiInfo.type_string: "QString",
        model.UmlXmiInfo.type_unlimited: "int",
    }

    keyword_map = {
        "class": "class2",
        "default": "default2",
        "interface": "interface2",
        "namespace": "namespace2",
        "slots": "slots2",
        "template": "template2",
    }

    plural_map = {
        "provided": "provided",
        "Provided": "Provided",
        "required": "required",
        "Required": "Required",
        "Dependency": "Dependencies",
        "Class": "Classes",
    }

    def __init__(self) -> None:
        self.model = UmlModel()

    def to_model(self, xmi_file: str, key: str):
        tree = ET.parse(xmi_file)
        root = tree.getroot()

        element = root.find(UmlXmiInfo.type_package, UmlXmiInfo.ns)
        if element is not None:
            package = self.__parse_package(element)
            self.model.package = package
            self.model.packages.append(package)
            self.model.packageDict[key] = package
            return

        element = root.find(UmlXmiInfo.type_profile, UmlXmiInfo.ns)
        if element is not None:
            profile = self.__parse_profile(element)
            self.model.profile = profile
            self.model.profiles.append(profile)
            self.model.profileDict[key] = profile

    def model_to_json(self, json_file: str, key: str):
        FileUtil.write_json_file(json_file, self.model.to_json(key))

    def models_to_json(self, json_file: str):
        FileUtil.write_json_file(json_file, self.model.to_json_all())

    @staticmethod
    def get_attr_is_array(attr: model.UmlProperty) -> bool:
        is_array = attr.upperValue is not None
        return is_array

    @staticmethod
    def get_attr_is_primitive(attr: model.UmlProperty) -> bool:
        is_array = UmlManager.get_attr_is_array(attr)
        if is_array:
            return False

        # primitive types
        attr_type: str = UmlManager.primitive_types.get(attr.type.href)
        return attr_type is not None

    @staticmethod
    def get_attr_identity(attr: model.UmlProperty) -> str:
        is_array = UmlManager.get_attr_is_array(attr)
        text = UmlManager.keyword_map.get(attr.name)
        if text is not None:
            if is_array:
                return UmlManager.__get_plural(attr.name)
            else:
                return f"{attr.name}2"

        if is_array:
            if UmlManager.keyword_map.get(f"{attr.name}s") is not None:
                return f"{attr.name}s2"
            return UmlManager.__get_plural(attr.name)

        return attr.name
    
    @staticmethod
    def get_op_is_array(op: model.UmlOperation) -> bool:
        is_array = False
        for param in op.ownedParameters:
            if param.direction == "return":
                is_array = param.upperValue is not None
                return is_array

        return is_array
    
    @staticmethod
    def get_op_identity(op: model.UmlOperation) -> str:
        is_array = UmlManager.get_op_is_array(op)
        if op.name.endswith("s"):
            is_array = False

        text = UmlManager.keyword_map.get(op.name)
        if text is not None:
            if is_array:
                return UmlManager.__get_plural(op.name)
            else:
                return f"{op.name}2"

        if is_array:
            if UmlManager.keyword_map.get(f"{op.name}s") is not None:
                return f"{op.name}s2"
            return UmlManager.__get_plural(op.name)

        return op.name

    @staticmethod
    def get_attr_type(attr: model.UmlProperty, model: UmlModel) -> str:
        is_array = UmlManager.get_attr_is_array(attr)
        if is_array:
            return ""

        # primitive types
        attr_type: None | str = UmlManager.primitive_types.get(attr.type.href)
        if attr_type is not None:
            return attr_type

        # enum types
        attr_type = model.enums.get(attr.type.href)
        if attr_type is not None:
            return attr_type.name

        return ""

    @staticmethod
    def get_attr_is_enum(attr: model.UmlProperty, model: UmlModel) -> bool:
        is_array = UmlManager.get_attr_is_array(attr)
        if is_array:
            return False

        # primitive types
        attr_type: str = model.enums.get(attr.type.href)
        return attr_type is not None

    @staticmethod
    def get_attr_enum(attr: model.UmlProperty, model: UmlModel) -> UmlEnumeration:
        return model.enums.get(attr.type.href)

    def __parse_package(self, element: ET.Element) -> UmlPackage:
        item: UmlPackage = UmlPackage()

        # attributes
        for key, value in element.attrib.items():
            match key:
                case UmlXmiInfo.attr_xmi_type:
                    continue
                case UmlXmiInfo.attr_xmi_id:
                    item.xmi_id = value
                case "URI":
                    item.uri = value
                case "name":
                    item.name = value
                case _:
                    print(
                        f"{sys._getframe().f_code.co_name} unknown the attribute: {key}"
                    )

        # elements
        for child in element:
            match child.tag:
                case "ownedComment":
                    sub_item = self.__parse_comment(child)
                    item.ownedComment = sub_item
                case "packageImport":
                    sub_item = self.__parse_package_import(child)
                    item.packageImports.append(sub_item)
                case "packagedElement":
                    sub_item = self.__parse_package_element(child, item)
                    item.packagedElements[sub_item.xmi_id] = sub_item
                case _:
                    print(
                        f"{sys._getframe().f_code.co_name} unknown the element: {child.tag}"
                    )

        item.packageImports.sort(key=lambda x: x.xmi_id)
        return item

    def __parse_package_import(self, element: ET.Element) -> UmlPackageImport:
        item: UmlPackageImport = UmlPackageImport()

        # attributes
        for key, value in element.attrib.items():
            match key:
                case UmlXmiInfo.attr_xmi_type:
                    continue
                case UmlXmiInfo.attr_xmi_id:
                    item.xmi_id = value
                case "importedPackage":
                    item.importedPackage = value
                case _:
                    print(
                        f"{sys._getframe().f_code.co_name} unknown the attribute: {key}"
                    )
        return item

    def __parse_package_element(
        self, element: ET.Element, package: model.UmlPackage
    ) -> UmlElement:
        xmi_type = element.attrib.get(UmlXmiInfo.attr_xmi_type)
        match xmi_type:
            case UmlXmiInfo.type_package:
                return self.__parse_package(element)
            case UmlXmiInfo.type_class:
                return self.__parse_class(element, package)
            case UmlXmiInfo.type_data_type:
                return self.__parse_class(element, package)
            case UmlXmiInfo.type_enumeration:
                return self.__parse_enumeration(element)
            case UmlXmiInfo.type_association:
                return self.__parse_association(element)
            case UmlXmiInfo.type_primitive_type:
                return self.__parse_primitive_type(element)
            case UmlXmiInfo.type_extension:
                return self.__parse_extension(element)
            case UmlXmiInfo.type_stereotype:
                return self.__parse_stereotype(element)
            case _:
                print(
                    f"{sys._getframe().f_code.co_name} unknown the package element: {xmi_type}"
                )

    def __parse_class(self, element: ET.Element, package: model.UmlPackage) -> UmlClass:
        item: UmlClass = UmlClass()
        item.package = package

        # attributes
        for key, value in element.attrib.items():
            match key:
                case UmlXmiInfo.attr_xmi_type:
                    item.xmi_type = value
                case UmlXmiInfo.attr_xmi_id:
                    item.xmi_id = value
                case "name":
                    item.name = value
                case "isAbstract":
                    item.isAbstract = value
                case _:
                    print(
                        f"{sys._getframe().f_code.co_name} unknown the attribute: {key}"
                    )

        # elements
        for child in element:
            match child.tag:
                case "ownedComment":
                    sub_item = self.__parse_comment(child)
                    item.ownedComment = sub_item
                case "ownedRule":
                    sub_item = self.__parse_constraint(child)
                    item.ownedRules.append(sub_item)
                case "generalization":
                    sub_item = self.__parse_generalization(child)
                    item.generalizations.append(sub_item)
                case "ownedAttribute":
                    sub_item = self.__parse_property(child)
                    item.ownedAttributes.append(sub_item)
                case "ownedOperation":
                    sub_item = self.__parse_operation(child)
                    item.ownedOperations.append(sub_item)
                case _:
                    print(
                        f"{sys._getframe().f_code.co_name} unknown the element: {child.tag}"
                    )

        item.generalizations.sort(key=lambda x: x.xmi_id)
        self.model.classes[item.name] = item
        return item

    def __parse_comment(self, element: ET.Element) -> model.UmlComment:
        item: model.UmlComment = model.UmlComment()

        # attributes
        for key, value in element.attrib.items():
            match key:
                case UmlXmiInfo.attr_xmi_type:
                    continue
                case UmlXmiInfo.attr_xmi_id:
                    item.xmi_id = value
                case "body":
                    item.body = value
                case "annotatedElement":
                    item.annotatedElement = value
                case _:
                    print(
                        f"{sys._getframe().f_code.co_name} unknown the attribute: {key}"
                    )

        # elements
        for child in element:
            match child.tag:
                case "annotatedElement":
                    sub_item = self.__parse_ref(child)
                    item.annotatedElement = sub_item
                case "body":
                    item.body = child.text
                case _:
                    print(
                        f"{sys._getframe().f_code.co_name} unknown the element: {child.tag}"
                    )

        return item

    def __parse_ref(self, element: ET.Element) -> str:
        item: str = ""

        # attributes
        for key, value in element.attrib.items():
            match key:
                case UmlXmiInfo.attr_xmi_id_ref:
                    item = value
                case "href":
                    item = value
                case _:
                    print(
                        f"{sys._getframe().f_code.co_name} unknown the attribute: {key}"
                    )
                    traceback.print_stack()

        return item

    def __parse_constraint(self, element: ET.Element) -> model.UmlConstraint:
        item: model.UmlConstraint = model.UmlConstraint()

        # attributes
        for key, value in element.attrib.items():
            match key:
                case UmlXmiInfo.attr_xmi_type:
                    continue
                case UmlXmiInfo.attr_xmi_id:
                    item.xmi_id = value
                case "name":
                    item.name = value
                case "constrainedElement":
                    item.name = value
                case _:
                    print(
                        f"{sys._getframe().f_code.co_name} unknown the attribute: {key}"
                    )

        # elements
        for child in element:
            match child.tag:
                case "constrainedElement":
                    sub_item = self.__parse_ref(child)
                    item.constrainedElement = sub_item
                case "ownedComment":
                    sub_item = self.__parse_comment(child)
                    item.ownedComment = sub_item
                case "specification":
                    sub_item = self.__parse_opaque_expression(child)
                    item.specification = sub_item
                case _:
                    print(
                        f"{sys._getframe().f_code.co_name} unknown the element: {child.tag}"
                    )

        return item

    def __parse_generalization(self, element: ET.Element) -> model.UmlGeneralization:
        item: model.UmlGeneralization = model.UmlGeneralization()

        # attributes
        for key, value in element.attrib.items():
            match key:
                case UmlXmiInfo.attr_xmi_type:
                    continue
                case UmlXmiInfo.attr_xmi_id:
                    item.xmi_id = value
                case "general":
                    item.general = value
                case _:
                    print(
                        f"{sys._getframe().f_code.co_name} unknown the attribute: {key}"
                    )

        # elements
        for child in element:
            match child.tag:
                case "general":
                    item.general = child.attrib.get("href")
                case _:
                    print(
                        f"{sys._getframe().f_code.co_name} unknown the element: {child.tag}"
                    )

        return item

    def __parse_property(self, element: ET.Element) -> model.UmlProperty:
        item: model.UmlProperty = model.UmlProperty()

        # attributes
        for key, value in element.attrib.items():
            match key:
                case UmlXmiInfo.attr_xmi_type:
                    continue
                case UmlXmiInfo.attr_xmi_id:
                    item.xmi_id = value
                case "name":
                    item.name = value
                case "aggregation":
                    item.aggregation = value
                case "type":
                    item.type = self.__parse_type(value)
                case "association":
                    item.association = value
                case "subsettedProperty":
                    item.subsettedProperty = value
                case "isReadOnly":
                    item.isReadOnly = value
                case "isDerived":
                    item.isDerived = value
                case "isDerivedUnion":
                    item.isDerivedUnion = value
                case "isOrdered":
                    item.isOrdered = value
                case "isUnique":
                    item.isUnique = value
                case "visibility":
                    item.visibility = value
                case "redefinedProperty":
                    item.redefinedProperty = value
                case _:
                    print(
                        f"{sys._getframe().f_code.co_name} unknown the attribute: {key}"
                    )

        # elements
        for child in element:
            match child.tag:
                case "ownedComment":
                    sub_item = self.__parse_comment(child)
                    item.ownedComment = sub_item
                case "type":
                    sub_item = self.__parse_type(child)
                    item.type = sub_item
                case "subsettedProperty":
                    sub_item = self.__parse_ref(child)
                    item.subsettedProperty = sub_item
                case "lowerValue":
                    sub_item = self.__parse_value(child)
                    item.lowerValue = sub_item
                case "upperValue":
                    sub_item = self.__parse_value(child)
                    item.upperValue = sub_item
                case "defaultValue":
                    sub_item = self.__parse_value(child)
                    item.defaultValue = sub_item
                case "redefinedProperty":
                    item.redefinedProperty = child.text
                case _:
                    print(
                        f"{sys._getframe().f_code.co_name} unknown the element: {child.tag}"
                    )

        return item

    def __parse_operation(self, element: ET.Element) -> model.UmlOperation:
        item: model.UmlOperation = model.UmlOperation()

        # attributes
        for key, value in element.attrib.items():
            match key:
                case UmlXmiInfo.attr_xmi_type:
                    continue
                case UmlXmiInfo.attr_xmi_id:
                    item.xmi_id = value
                case "name":
                    item.name = value
                case "isAbstract":
                    item.isAbstract = value
                case "isQuery":
                    item.isQuery = value
                case "precondition":
                    item.precondition = value
                case "redefinedOperation":
                    item.redefinedOperation = value
                case "bodyCondition":
                    item.bodyCondition = value
                case _:
                    print(
                        f"{sys._getframe().f_code.co_name} unknown the attribute: {key}"
                    )

        # elements
        for child in element:
            match child.tag:
                case "ownedComment":
                    sub_item = self.__parse_comment(child)
                    item.ownedComment = sub_item
                case "ownedRule":
                    sub_item = self.__parse_constraint(child)
                    item.ownedRules.append(sub_item)
                case "ownedParameter":
                    sub_item = self.__parse_parameter(child)
                    item.ownedParameters.append(sub_item)
                case _:
                    print(
                        f"{sys._getframe().f_code.co_name} unknown the element: {child.tag}"
                    )

        return item

    def __parse_opaque_expression(
        self, element: ET.Element
    ) -> model.UmlOpaqueExpression:
        item: model.UmlOpaqueExpression = model.UmlOpaqueExpression()

        # attributes
        for key, value in element.attrib.items():
            match key:
                case UmlXmiInfo.attr_xmi_type:
                    continue
                case UmlXmiInfo.attr_xmi_id:
                    item.xmi_id = value
                case "name":
                    item.name = value
                case "language":
                    item.language = value
                case _:
                    print(
                        f"{sys._getframe().f_code.co_name} unknown the attribute: {key}"
                    )

        # elements
        for child in element:
            match child.tag:
                case "body":
                    item.body = child.text
                case "language":
                    item.language = child.text
                case "ownedComment":
                    sub_item = self.__parse_comment(child)
                    item.ownedComment = sub_item
                case _:
                    print(
                        f"{sys._getframe().f_code.co_name} unknown the element: {child.tag}"
                    )

        return item

    def __parse_parameter(self, element: ET.Element) -> model.UmlParameter:
        item: model.UmlParameter = model.UmlParameter()

        # attributes
        for key, value in element.attrib.items():
            match key:
                case UmlXmiInfo.attr_xmi_type:
                    continue
                case UmlXmiInfo.attr_xmi_id:
                    item.xmi_id = value
                case "name":
                    item.name = value
                case "direction":
                    item.direction = value
                case "type":
                    item.type = self.__parse_type(value)
                case "isOrdered":
                    item.isOrdered = value
                case "isUnique":
                    item.isUnique = value
                case _:
                    print(
                        f"{sys._getframe().f_code.co_name} unknown the attribute: {key}"
                    )

        # elements
        for child in element:
            match child.tag:
                case "type":
                    sub_item = self.__parse_type(child)
                    item.type = sub_item
                case "lowerValue":
                    sub_item = self.__parse_value(child)
                    item.lowerValue = sub_item
                case "upperValue":
                    sub_item = self.__parse_value(child)
                    item.upperValue = sub_item
                case _:
                    print(
                        f"{sys._getframe().f_code.co_name} unknown the element: {child.tag}"
                    )

        return item

    def __parse_value(self, element: ET.Element) -> model.UmlValue:
        item: model.UmlValue = model.UmlValue()

        # attributes
        for key, value in element.attrib.items():
            match key:
                case UmlXmiInfo.attr_xmi_type:
                    item.xmi_type = value
                case UmlXmiInfo.attr_xmi_id:
                    item.xmi_id = value
                case "name":
                    item.name = value
                case "instance":
                    item.instance = value
                case "type":
                    item.type = value
                case "value":
                    item.value = value
                case _:
                    print(f"{sys._getframe().f_code.co_name} unknown attribute: {key}")
                    traceback.print_stack()

        return item

    def __parse_association(self, element: ET.Element) -> model.UmlAssociation:
        item: model.UmlAssociation = model.UmlAssociation()

        # attributes
        for key, value in element.attrib.items():
            match key:
                case UmlXmiInfo.attr_xmi_type:
                    continue
                case UmlXmiInfo.attr_xmi_id:
                    item.xmi_id = value
                case "name":
                    item.name = value
                case "memberEnd":
                    item.memberEnd = value.split(" ")
                case "isDerived":
                    item.isDerived = value
                case _:
                    print(
                        f"{sys._getframe().f_code.co_name} unknown the attribute: {key}"
                    )

        # elements
        for child in element:
            match child.tag:
                case "memberEnd":
                    sub_item = self.__parse_ref(child)
                    item.memberEnd.append(sub_item)
                case "ownedEnd":
                    sub_item = self.__parse_property(child)
                    item.ownedEnd = sub_item
                case "ownedComment":
                    sub_item = self.__parse_comment(child)
                    item.ownedComment = sub_item
                case "generalization":
                    sub_item = self.__parse_generalization(child)
                    item.generalization = sub_item
                case _:
                    print(
                        f"{sys._getframe().f_code.co_name} unknown the element: {child.tag}"
                    )
        return item

    def __parse_enumeration(self, element: ET.Element) -> model.UmlEnumeration:
        item: model.UmlEnumeration = model.UmlEnumeration()

        # attributes
        for key, value in element.attrib.items():
            match key:
                case UmlXmiInfo.attr_xmi_type:
                    continue
                case UmlXmiInfo.attr_xmi_id:
                    item.xmi_id = value
                case "name":
                    item.name = value
                case _:
                    print(
                        f"{sys._getframe().f_code.co_name} unknown the attribute: {key}"
                    )

        # elements
        for child in element:
            match child.tag:
                case "ownedComment":
                    sub_item = self.__parse_comment(child)
                    item.ownedComment = sub_item
                case "ownedLiteral":
                    sub_item = self.__parse_enumeration_literal(child)
                    item.ownedLiterals.append(sub_item)
                case _:
                    print(
                        f"{sys._getframe().f_code.co_name} unknown the element: {child.tag}"
                    )

        self.model.enums[item.name] = item
        return item

    def __parse_enumeration_literal(
        self, element: ET.Element
    ) -> model.UmlEnumerationLiteral:
        item: model.UmlEnumerationLiteral = model.UmlEnumerationLiteral()

        # attributes
        for key, value in element.attrib.items():
            match key:
                case UmlXmiInfo.attr_xmi_type:
                    continue
                case UmlXmiInfo.attr_xmi_id:
                    item.xmi_id = value
                case "name":
                    item.name = value
                case _:
                    print(
                        f"{sys._getframe().f_code.co_name} unknown the attribute: {key}"
                    )

        # elements
        for child in element:
            match child.tag:
                case "ownedComment":
                    sub_item = self.__parse_comment(child)
                    item.ownedComment = sub_item
                case _:
                    print(
                        f"{sys._getframe().f_code.co_name} unknown the element: {child.tag}"
                    )
        return item

    def __parse_extension(self, element: ET.Element) -> model.UmlExtension:
        item: model.UmlExtension = model.UmlExtension()

        # attributes
        for key, value in element.attrib.items():
            match key:
                case UmlXmiInfo.attr_xmi_type:
                    continue
                case UmlXmiInfo.attr_xmi_id:
                    item.xmi_id = value
                case "name":
                    item.name = value
                case "memberEnd":
                    item.memberEnd = value
                case _:
                    print(
                        f"{sys._getframe().f_code.co_name} unknown the attribute: {key}"
                    )

        # elements
        for child in element:
            match child.tag:
                case "ownedEnd":
                    sub_item = self.__parse_extension_end(child)
                    item.ownedEnd = sub_item
                case _:
                    print(
                        f"{sys._getframe().f_code.co_name} unknown the element: {child.tag}"
                    )
        return item

    def __parse_extension_end(self, element: ET.Element) -> model.UmlExtensionEnd:
        item: model.UmlExtensionEnd = model.UmlExtensionEnd()

        # attributes
        for key, value in element.attrib.items():
            match key:
                case UmlXmiInfo.attr_xmi_type:
                    continue
                case UmlXmiInfo.attr_xmi_id:
                    item.xmi_id = value
                case "name":
                    item.name = value
                case "type":
                    item.type = value
                case "aggregation":
                    item.aggregation = value
                case "association":
                    item.association = value
                case _:
                    print(
                        f"{sys._getframe().f_code.co_name} unknown the attribute: {key}"
                    )

        # elements
        for child in element:
            match child.tag:
                case "lowerValue":
                    sub_item = self.__parse_value(child)
                    item.lowerValue = sub_item
                case _:
                    print(
                        f"{sys._getframe().f_code.co_name} unknown the element: {child.tag}"
                    )
        return item

    def __parse_profile(self, element: ET.Element) -> model.UmlProfile:
        item: model.UmlProfile = model.UmlProfile()

        # attributes
        for key, value in element.attrib.items():
            match key:
                case UmlXmiInfo.attr_xmi_type:
                    continue
                case UmlXmiInfo.attr_xmi_id:
                    item.xmi_id = value
                case "name":
                    item.name = value
                case "metamodelReference":
                    item.metamodelReference = value
                case "URI":
                    item.uri = value
                case _:
                    print(
                        f"{sys._getframe().f_code.co_name} unknown the attribute: {key}"
                    )

        # elements
        for child in element:
            match child.tag:
                case "packageImport":
                    sub_item = self.__parse_package_import(child)
                    item.packageImports.append(sub_item)
                case "packagedElement":
                    sub_item = self.__parse_package_element(child, item)
                    item.packagedElements[sub_item.xmi_id] = sub_item
                case _:
                    print(
                        f"{sys._getframe().f_code.co_name} unknown the element: {child.tag}"
                    )

        item.packageImports.sort(key=lambda x: x.xmi_id)
        return item

    def __parse_stereotype(self, element: ET.Element | str) -> model.UmlStereotype:
        item: model.UmlStereotype = model.UmlStereotype()

        # attributes
        for key, value in element.attrib.items():
            match key:
                case UmlXmiInfo.attr_xmi_type:
                    item.xmi_type = value
                case UmlXmiInfo.attr_xmi_id:
                    item.xmi_id = value
                case "name":
                    item.name = value
                case _:
                    print(
                        f"{sys._getframe().f_code.co_name} unknown the attribute: {key}"
                    )

        # elements
        for child in element:
            match child.tag:
                case "ownedAttribute":
                    sub_item = self.__parse_property(child)
                    item.ownedAttribute = sub_item
                case "generalization":
                    sub_item = self.__parse_generalization(child)
                    item.generalizations.append(sub_item)
                case _:
                    print(
                        f"{sys._getframe().f_code.co_name} unknown the element: {child.tag}"
                    )

        item.generalizations.sort(key=lambda x: x.xmi_id)
        return item

    def __parse_primitive_type(self, element: ET.Element) -> model.UmlPrimitiveType:
        item: model.UmlPrimitiveType = model.UmlPrimitiveType()

        # attributes
        for key, value in element.attrib.items():
            match key:
                case UmlXmiInfo.attr_xmi_type:
                    continue
                case UmlXmiInfo.attr_xmi_id:
                    item.xmi_id = value
                case "name":
                    item.name = value
                case _:
                    print(
                        f"{sys._getframe().f_code.co_name} unknown the attribute: {key}"
                    )

        return item

    def __parse_type(self, element: ET.Element | str) -> model.UmlType:
        item: model.UmlType = model.UmlType()

        if isinstance(element, str):
            item.href = element
            return item

        # attributes
        for key, value in element.attrib.items():
            match key:
                case UmlXmiInfo.attr_xmi_type:
                    item.xmi_type = value
                case "href":
                    item.href = value
                case _:
                    print(
                        f"{sys._getframe().f_code.co_name} unknown the attribute: {key}"
                    )

        return item

    def __get_plural(name: str) -> str:
        for key, value in UmlManager.plural_map.items():
            if name.endswith(key):
                return name.replace(key, value)

        if name.endswith("s"):
            return name
        return f"{name}s"
